# Classe per l'applicazione della regressione lineare

require 'utility/vec_utils'

class LinearRegression

  attr_accessor :beta1,:beta0, :epsilon

  def initialize(train_matrix,train_vector)
    @beta1= self.pendenza(train_vector, train_matrix)
    @beta0= self.intercetta(train_vector, train_matrix)
    @epsilon= 0
    @epsilon= errore(train_vector, train_matrix)
  end
  # Funzione di update
  def update(train_matrix, train_vector)
     b1= self.pendenza(train_vector, train_matrix)
     ubeta1= (self.beta1+b1)/2
     b0= self.intercetta(train_vector, train_matrix, ubeta1)
     ubeta0= (b0+self.beta0)/2
     
    self.beta0= ubeta0
    self.beta1= ubeta1
  end
  # Funzione per il calcolo della pendenza
  def pendenza(train_vector_y, train_matrix)
    pendenza= 0

    train_vector_x= VecUtils.matrix_av_vector(train_matrix)
    av_train_vector_x= VecUtils.average(train_vector_x)

    av_train_vector_y= VecUtils.average(train_vector_y)

    nominatore = 0
    denominatore = 0

    (0..train_vector_y.size-1).each{|i|
      nominatore= nominatore + ((train_vector_x[i]-av_train_vector_x)*(train_vector_y[i]-av_train_vector_y))
      denominatore= denominatore + (train_vector_x[i]-av_train_vector_x)**2
    }
    pendenza= nominatore/denominatore

    return pendenza
  end
  # Funzione per il calcolo dell'intercetta
  def intercetta(train_vector_y, train_matrix,beta=self.beta1)
    intercetta = 0

    train_vector_x= VecUtils.matrix_av_vector(train_matrix)
    av_train_vector_x= VecUtils.average(train_vector_x)

    av_train_vector_y= VecUtils.average(train_vector_y)

    intercetta= (av_train_vector_y - beta*av_train_vector_x)

    return intercetta
  end
  # Funzione che calcola l'errore
  def errore(vector_y, matrix)
    regressione= self.regressione(matrix)
    media_y= VecUtils.average(vector_y)

    somma_errore_quadratico= 0
    varianza= 0

    (0..vector_y.size-1).each{|i|
      somma_errore_quadratico= somma_errore_quadratico + ((vector_y[i]-regressione[i])**2)
    }

    (0..vector_y.size-1).each{|i|
      varianza= varianza + ((vector_y[i]-media_y)**2)
    }

    errore= (varianza-somma_errore_quadratico)/varianza
    
    return errore
  end
  # Funzione che calcola la regressione
  def regressione(matrix)
    regressione= []

    vector_x= VecUtils.matrix_av_vector(matrix)
    vector_x.each{|x|

      y= self.beta0+self.beta1*x # + self.epsilon
      regressione.push(y.round)
 
    }
    return regressione
  end
  def beta1
    @beta1
  end
  def beta0
    @beta0
  end
  def epsilon
    @epsilon
  end
  def LinearRegression.test()
    x = [[71,73,64,65,61,70,65,72,63,67,64]]
    y = [160,183,154, 168, 159, 180, 145, 210, 132, 168, 141]

    r= LinearRegression.new(x,y)

    p '<---------------------------->'
    p 'Test regressione lineare'
    p '<--------->'
    p 'Dati'
    p 'intercetta'
    p r.beta0
    p 'pendenza'
    p r.beta1
    p 'errore'
    p r.epsilon
    p 'fine dati'
    p '<--------->'
    p 'test con valore 70'
    p 'valore atteso: [176.51,185]'
    p r.regressione([[70,72]])
    p '<---------------------------->'
  end
end
